/*
 * File Name：BaseRequest.java
 * Copyright：Copyright 2008-2015 CiWong.Inc. All Rights Reserved.
 * Description： BaseRequest.java
 * Modify By：Administrator
 * Modify Date：2015年4月6日
 * Modify Type：Add
 */
package com.ciwong.libs.utils.volley;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import com.android.volley.AuthFailureError;
import com.android.volley.DefaultRetryPolicy;
import com.android.volley.NetworkResponse;
import com.android.volley.ParseError;
import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.Response.ErrorListener;
import com.android.volley.Response.Listener;
import com.android.volley.RetryPolicy;
import com.android.volley.VolleyLog;
import com.android.volley.toolbox.HttpHeaderParser;
import com.google.gson.Gson;

/**
 * Volley请求基础类
 * 
 * @author Administrator
 * @version ciwong v.1.0 2015年4月6日
 * @since ciwong v.1.0
 */
public class BaseRequest extends Request<Object>
{
    /**
     * 模块名称
     */
    public static final String REQUEST_MODULES = "REQUEST_MODULES";

    /**
     * 请求动作
     */
    public static final String REQUEST_ACTION = "REQUEST_ACTION";

    /**
     * HTTP超时时间
     */
    public static final int HTTP_TIMEOUT_MS = 6000;

    /**
     * 最大重连次数
     */
    public static final int HTTP_MAX_RETRIES = 3;

    /**
     * HTTP超时时间
     */
    public static final int HTTP_TIMEOUT_MS_POST = 120000;

    /**
     * 最大重连次数
     */
    public static final int HTTP_MAX_RETRIES_POST = 0;

    /**
     * 响应结果类型：字符串
     */
    public static final int RESPONSE_DATA_TYPE_STRING = 1;

    /**
     * 响应结果类型：指定对象
     */
    public static final int RESPONSE_DATA_TYPE_OBJECT = 2;

    /**
     * 响应结果类型：指定对象数组
     */
    public static final int RESPONSE_DATA_TYPE_ARRAY = 3;

    /**
     * 响应结果类型：sax handler(调用者传入的解析xml的Handler)
     */
    public static final int RESPONSE_DATA_TYPE_XML_HANDLER = 4;

    /**
     * 响应数据字段：ret
     */
    public static final String RESPONSE_DATA_COLUMN_RET = "ret";

    /**
     * 响应数据字段：errcode
     */
    public static final String RESPONSE_DATA_COLUMN_ERRCODE = "errcode";

    /**
     * 响应数据字段：msg
     */
    public static final String RESPONSE_DATA_COLUMN_MSG = "msg";

    /**
     * 响应数据字段：data
     */
    public static final String RESPONSE_DATA_COLUMN_DATA = "data";

    /**
     * 是否需要拼接Post参数方式
     */
    private static boolean isConcatPostParams;

    /**
     * 验证信息
     */
    private static VerifyInfo verifyInfo;

    private static VerifyInfoListener l;

    private static Map<String, CWAction> mCWActions;

    private Listener<Object> listener;

    private Map<String, String> params;

    /**
     * 网络请求优先级
     */
    private Priority priority = Request.Priority.NORMAL;

    /**
     * http网络请求重试策略
     */
    private RetryPolicy retryPolicy = new DefaultRetryPolicy(HTTP_TIMEOUT_MS,
            HTTP_MAX_RETRIES, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT);

    /**
     * http网络请求重试策略POST
     */
    private RetryPolicy retryPolicyPost = new DefaultRetryPolicy(
            HTTP_TIMEOUT_MS_POST, HTTP_MAX_RETRIES_POST,
            DefaultRetryPolicy.DEFAULT_BACKOFF_MULT);

    /**
     * 响应结果类型
     */
    private int responseDataType = RESPONSE_DATA_TYPE_STRING;

    /**
     * 响应结果:对象
     */
    private Class<?> responseClazz;

    /**
     * 响应结果:对象数组
     */
    private Type responseType;

    /**
     * 响应结果:XmlHandler
     */
    private XmlHandler xmlHandler;

    /**
     * 网络请求响应回调接口
     */
    private ResponseCallback callback;

    /**
     * 
     * 构造函数
     * 
     * @param url
     *            自定义网络请求地址
     * @param errorListener
     *            网络请求错误响应回调接口
     * @param callback
     *            网络请求响应回调接口
     */
    public BaseRequest(String url, ErrorListener errorListener,
            ResponseCallback callback)
    {
        super(Request.Method.GET, url, errorListener);
        this.callback = callback;
        init(Request.Method.GET);
    }

    /**
     * 
     * 构造函数
     * 
     * @param method
     *            请求方法
     * @param url
     *            自定义网络请求地址
     * @param errorListener
     *            网络请求错误响应回调接口
     * @param callback
     *            网络请求响应回调接口
     */
    public BaseRequest(int method, String url, ErrorListener errorListener,
            ResponseCallback callback)
    {
        super(method, url, errorListener);
        this.callback = callback;
        init(method);
    }

    /**
     * 
     * 构造函数
     * 
     * @param isVerifyAction
     *            是否添加Verify动作
     * @param params
     *            请求参数
     * @param errorListener
     *            网络请求错误响应回调接口
     * @param callback
     *            网络请求响应回调接口
     */
    public BaseRequest(boolean isVerifyAction, Map<String, String> params,
            ErrorListener errorListener, ResponseCallback callback)
    {
        this(isVerifyAction, Request.Method.GET, params, errorListener,
                callback);
    }

    /**
     * 
     * 构造函数
     * 
     * @param isVerifyAction
     *            是否添加Verify动作
     * @param method
     *            请求方法
     * @param params
     *            请求参数
     * @param errorListener
     *            网络请求错误响应回调接口
     * @param callback
     *            网络请求响应回调接口
     */
    public BaseRequest(boolean isVerifyAction, int method,
            Map<String, String> params, ErrorListener errorListener,
            ResponseCallback callback)
    {
        super(method, buildUrl(isVerifyAction, method, params), errorListener);
        this.params = params;
        this.callback = callback;
        init(method);
    }

    @Override
    protected void deliverResponse(Object response)
    {
        listener.onResponse(response);
    }

    @Override
    protected Response<Object> parseNetworkResponse(NetworkResponse response)
    {
        byte[] b = null;
        if (HttpHelper.isGZIP(response.headers))
        {
            b = HttpHelper.unGZIP(response.data);
        }
        else
        {
            b = response.data;
        }
        String parsed;
        try
        {
            parsed = new String(b,
                    HttpHeaderParser.parseCharset(response.headers));
        }
        catch (UnsupportedEncodingException e)
        {
            e.printStackTrace();
            parsed = new String(b);
        }
        switch (responseDataType)
        {
            case RESPONSE_DATA_TYPE_ARRAY:
                if (responseType != null)
                {
                    try
                    {
                        return Response.success((Object) new JSONArray(parsed),
                                HttpHeaderParser.parseCacheHeaders(response));
                    }
                    catch (JSONException e)
                    {
                        e.printStackTrace();
                        return Response.error(new ParseError(e));
                    }
                }
                break;

            case RESPONSE_DATA_TYPE_STRING:
                return Response.success((Object) parsed,
                        HttpHeaderParser.parseCacheHeaders(response));

            case RESPONSE_DATA_TYPE_OBJECT:
                try
                {
                    return Response.success((Object) new JSONObject(parsed),
                            HttpHeaderParser.parseCacheHeaders(response));
                }
                catch (JSONException e)
                {
                    e.printStackTrace();
                    return Response.error(new ParseError(e));
                }

            case RESPONSE_DATA_TYPE_XML_HANDLER:
                try
                {
                    InputStream stream = new ByteArrayInputStream(response.data);
                    SAXParserFactory saxparserfactory = SAXParserFactory
                            .newInstance();
                    SAXParser saxparser = saxparserfactory.newSAXParser();
                    saxparser.parse(stream, xmlHandler);
                }
                catch (ParserConfigurationException e)
                {
                    e.printStackTrace();
                    return Response.error(new ParseError(e));
                }
                catch (SAXException e)
                {
                    e.printStackTrace();
                    return Response.error(new ParseError(e));
                }
                catch (IOException e)
                {
                    e.printStackTrace();
                    return Response.error(new ParseError(e));
                }
                return Response.success((Object) xmlHandler,
                        HttpHeaderParser.parseCacheHeaders(response));

            default:
                break;
        }
        return null;
    }

    @Override
    protected Map<String, String> getParams() throws AuthFailureError
    {
        switch (getMethod())
        {
            case Request.Method.POST:
                return params;

            case Request.Method.GET:
                return super.getParams();

            default:
                return super.getParams();
        }
    }

    /**
     * 
     * 请求地址初始化
     * 
     * @param isVerifyAction
     *            是否添加Verify动作
     * @param params
     *            请求参数
     * @return 返回请求地址
     */
    public static String buildUrl(boolean isVerifyAction, int method,
            Map<String, String> params)
    {
        String url = null;
        String modules = null;
        String action = null;
        if (params != null && !params.isEmpty())
        {
            modules = params.get(REQUEST_MODULES);
            action = params.get(REQUEST_ACTION);
            params.remove(REQUEST_MODULES);
            params.remove(REQUEST_ACTION);
            CWAction cwAction = mCWActions == null ? null : mCWActions
                    .get(modules);
            if (cwAction != null)
            {
                url = cwAction.getActionUrl(action);
            }
            else
            {
                VolleyLog.e("请调用registAction()方法注册  %s  !", modules);
            }
            if (url != null)
            {
                if (isVerifyAction)
                {
                    if (BaseRequest.getVerifyInfo() == null)
                    {
                        if (!url.endsWith("?") && !url.contains("?"))
                        {
                            url += "?";
                        }
                    }
                    else
                    {
                        url += HttpHelper.getVerifyStr(BaseRequest
                                .getVerifyInfo());
                    }
                }
                else
                {
                    if (!url.endsWith("?") && !url.contains("?"))
                    {
                        url += "?";
                    }
                }
                switch (method)
                {
                    case Request.Method.POST:
                        url = HttpHelper.concatUrl(params, url);
                        break;

                    case Request.Method.GET:
                        url = HttpHelper.concatUrl(params, url);
                        break;

                    default:
                        url = HttpHelper.concatUrl(params, url);
                        break;
                }
            }
        }
        return url;
    }

    private void init(int method)
    {
        if (method == Request.Method.POST)
        {
            setRetryPolicy(retryPolicyPost);
        }
        else
        {
            setRetryPolicy(retryPolicy);
        }
        this.listener = new Response.Listener<Object>()
        {

            @Override
            public void onResponse(Object response)
            {
                switch (responseDataType)
                {
                    case RESPONSE_DATA_TYPE_STRING:
                        if (BaseRequest.this.callback != null)
                        {
                            BaseRequest.this.callback.success(response
                                    .toString());
                        }
                        break;

                    case RESPONSE_DATA_TYPE_OBJECT:
                        try
                        {
                            JSONObject json = (JSONObject) response;
                            int ret = json.getInt(RESPONSE_DATA_COLUMN_RET);
                            Object object = null;
                            if (ret == 0)
                            {
                                if (json.has(RESPONSE_DATA_COLUMN_DATA))
                                {
                                    Gson mGson = new Gson();
                                    if (responseClazz != null)
                                    {
                                        object = mGson
                                                .fromJson(
                                                        json.getString(RESPONSE_DATA_COLUMN_DATA),
                                                        responseClazz);
                                    }
                                    else if (responseType != null)
                                    {
                                        object = mGson
                                                .fromJson(
                                                        json.getString(RESPONSE_DATA_COLUMN_DATA),
                                                        responseType);
                                    }
                                }
                                if (BaseRequest.this.callback != null)
                                {
                                    int errorCode = 0;
                                    if (json.has(RESPONSE_DATA_COLUMN_ERRCODE))
                                    {
                                        errorCode = json
                                                .getInt(RESPONSE_DATA_COLUMN_ERRCODE);
                                    }
                                    BaseRequest.this.callback
                                            .success(
                                                    object,
                                                    ret,
                                                    errorCode,
                                                    json.getString(RESPONSE_DATA_COLUMN_MSG));
                                }
                            }
                            else
                            {
                                if (BaseRequest.this.callback != null)
                                {
                                    int errorCode = 0;
                                    if (json.has(RESPONSE_DATA_COLUMN_ERRCODE))
                                    {
                                        errorCode = json
                                                .getInt(RESPONSE_DATA_COLUMN_ERRCODE);
                                    }
                                    BaseRequest.this.callback
                                            .error(errorCode,
                                                    json.getString(RESPONSE_DATA_COLUMN_MSG));
                                }
                            }
                        }
                        catch (JSONException e)
                        {
                            e.printStackTrace();
                        }
                        break;

                    case RESPONSE_DATA_TYPE_XML_HANDLER:
                        if (BaseRequest.this.callback != null)
                        {
                            BaseRequest.this.callback
                                    .success((XmlHandler) response);
                        }
                        break;

                    case RESPONSE_DATA_TYPE_ARRAY:
                        break;

                    default:
                        break;
                }
            }
        };
    }

    /**
     * 
     * 设置此网络请求优先级(默认{@link Request.Priority.NORMAL})
     * 
     * @param priority
     *            {@link Request.Priority.LOW},{@link Request.Priority.NORMAL},
     *            {@link Request.Priority.HIGH},
     *            {@link Request.Priority.IMMEDIATE}
     */
    public void setPriority(Priority priority)
    {
        this.priority = priority;
    }

    @Override
    public Priority getPriority()
    {
        return priority;
    }

    /**
     * 
     * 获取网络请求验证信息
     * 
     * @return 返回网络请求验证信息
     */
    public static VerifyInfo getVerifyInfo()
    {
        if (BaseRequest.verifyInfo == null)
        {
            if (BaseRequest.l != null)
            {
                BaseRequest.verifyInfo = BaseRequest.l.getVerifyInfo();
            }
        }
        return BaseRequest.verifyInfo;
    }

    /**
     * 
     * 设置网络请求验证信息
     * 
     * @param verifyInfo
     *            网络请求验证信息
     */
    public static void setVerifyInfo(VerifyInfo verifyInfo)
    {
        BaseRequest.verifyInfo = verifyInfo;
    }

    /**
     * 
     * 设置网络请求验证信息监听
     * 
     * @param l
     *            验证信息监听
     */
    public static void setVerifyInfoListener(VerifyInfoListener l)
    {
        BaseRequest.l = l;
    }

    /**
     * 
     * 注册请求Action
     * 
     * @param clsName
     *            action的完整类名，作为hashmap里面的key
     * @param cwAction
     *            {@link CWAction}
     */
    public static void registAction(String clsName, CWAction cwAction)
    {
        if (mCWActions == null)
        {
            mCWActions = new HashMap<String, CWAction>();
        }
        if (!mCWActions.containsKey(clsName))
        {
            mCWActions.put(clsName, cwAction);
        }
    }

    /**
     * 设置调用者所需的数据的类型<br />
     * 
     * @param responseDataType
     *            数据类型<br />
     *            {@link HttpRequest#RESPONSE_DATA_TYPE_STRING}
     *            {@link HttpRequest#RESPONSE_DATA_TYPE_OBJECT}
     *            {@link HttpRequest#RESPONSE_DATA_TYPE_ARRAY}
     */
    public void setResponseDataType(int responseDataType)
    {
        this.responseDataType = responseDataType;
    }

    /**
     * 启用是否模拟拼接post参数的方式
     * 
     */
    public void enableConcatPostParams()
    {
        isConcatPostParams = true;
    }

    /**
     * 禁用是否模拟拼接post参数的方式
     * 
     */
    public void disableConcatPostParams()
    {
        isConcatPostParams = false;
    }

    /**
     * 在执行请求完后，若需要将数据转换成一个类对象，则必须要设置对象的Class .
     * 
     * @param clazz
     *            对象class
     */
    public void setResponseClazz(Class<?> responseClazz)
    {
        this.responseClazz = responseClazz;
    }

    /**
     * 
     * 在执行请求完后，若需要将数据转换成一个类对象，则必须要设置对象的Type<br />
     * 方法: Type t = new TypeToken<自己类的名称>(){}.getType();
     * 
     * @param responseType
     *            responseType
     */
    public void setResponseType(Type responseType)
    {
        this.responseType = responseType;
    }

    /**
     * 设置解析xml的sax Handler类
     * 
     * @param mHandler
     *            xml Handler
     */
    public void setXmlHandler(XmlHandler xmlHandler)
    {
        this.xmlHandler = xmlHandler;
    }

    /**
     * 验证信息实体类
     * 
     * @author bin
     * 
     */
    public static class VerifyInfo implements Serializable
    {
        private String clientId;

        private String accessToken;

        private String openId;

        private String oauthVersion;

        private String scope;

        private String clientIp;// ip地址

        private String expiresIn;

        private String refreshToken;

        private String tokenType;

        private int termType;

        public String getClientId()
        {
            return clientId;
        }

        public void setClientId(String clientId)
        {
            this.clientId = clientId;
        }

        public String getAccessToken()
        {
            return accessToken;
        }

        public void setAccessToken(String accessToken)
        {
            this.accessToken = accessToken;
        }

        public String getOpenId()
        {
            return openId;
        }

        public void setOpenId(String openId)
        {
            this.openId = openId;
        }

        public String getOauthVersion()
        {
            return oauthVersion;
        }

        public void setOauthVersion(String oauthVersion)
        {
            this.oauthVersion = oauthVersion;
        }

        public String getScope()
        {
            return scope;
        }

        public void setScope(String scope)
        {
            this.scope = scope;
        }

        public String getClientIp()
        {
            return clientIp;
        }

        public void setClientIp(String clientIp)
        {
            this.clientIp = clientIp;
        }

        public String getExpiresIn()
        {
            return expiresIn;
        }

        public void setExpiresIn(String expiresIn)
        {
            this.expiresIn = expiresIn;
        }

        public String getRefreshToken()
        {
            return refreshToken;
        }

        public void setRefreshToken(String refreshToken)
        {
            this.refreshToken = refreshToken;
        }

        public String getTokenType()
        {
            return tokenType;
        }

        public void setTokenType(String tokenType)
        {
            this.tokenType = tokenType;
        }

        public int getTermType()
        {
            return termType;
        }

        public void setTermType(int termType)
        {
            this.termType = termType;
        }
    }

    /**
     * 
     * 习网父Xml解释类
     * 
     * @author PLA-ZJLIU
     * 
     */
    public static class XmlHandler extends DefaultHandler
    {
        private int ret;// 返回说明

        private int errcode;// 二级错误字段【参数错误】说明

        private String msg;// 解释信息

        private String qName;

        /**
         * 返回说明
         * 
         * @return 返回说明
         */
        public int getRet()
        {
            return ret;
        }

        /**
         * 
         * 二级错误字段【参数错误】说明
         * 
         * @return 二级错误字段【参数错误】说明
         */
        public int getErrcode()
        {
            return errcode;
        }

        /**
         * 解释信息
         * 
         * @return 解释信息
         */
        public String getMsg()
        {
            return msg;
        }

        /**
         * <b style="color:red">重写时必须调用父类方法(super.startElement)</b><br />
         * {@inheritDoc}
         */
        @Override
        public void startElement(String uri, String localName, String qName,
                Attributes attributes) throws SAXException
        {
            this.qName = qName;
        }

        /**
         * <b style="color:red">重写时必须调用父类方法(super.characters)</b><br />
         * {@inheritDoc}
         */
        @Override
        public void characters(char[] ch, int start, int length)
                throws SAXException
        {
            String value = new String(ch, start, length);
            if (RESPONSE_DATA_COLUMN_RET.equals(qName))
            {
                ret = Integer.parseInt(value);
            }
            else if (RESPONSE_DATA_COLUMN_ERRCODE.equals(qName))
            {
                errcode = Integer.parseInt(value);
            }
            else if (RESPONSE_DATA_COLUMN_MSG.equals(qName))
            {
                msg = value;
            }
        }

        /**
         * <b style="color:red">重写时必须调用父类方法(super.endElement)</b><br />
         * {@inheritDoc}
         */
        @Override
        public void endElement(String uri, String localName, String qName)
                throws SAXException
        {
            this.qName = null;
        }
    }

    /**
     * 上传文件返回实体类
     * 
     * @author PLA-ZJLIU
     * 
     */
    public static class AttachmentUpload
    {
        private int result;

        private String msg;

        private Info info;

        public int getResult()
        {
            return result;
        }

        public void setResult(int result)
        {
            this.result = result;
        }

        public String getMsg()
        {
            return msg;
        }

        public void setMsg(String msg)
        {
            this.msg = msg;
        }

        public Info getInfo()
        {
            return info;
        }

        public void setInfo(Info info)
        {
            this.info = info;
        }

        /**
         * 文件信息
         * 
         * @author PLA-ZJLIU
         * 
         */
        public static class Info
        {
            private String image;

            private String file;

            private Size size;

            public String getImage()
            {
                return image;
            }

            public void setImage(String image)
            {
                this.image = image;
            }

            public String getFile()
            {
                return file;
            }

            public void setFile(String file)
            {
                this.file = file;
            }

            public Size getSize()
            {
                return size;
            }

            public void setSize(Size size)
            {
                this.size = size;
            }
        }

        /**
         * 大小
         * 
         * @author PLA-ZJLIU
         * 
         */
        public static class Size
        {
            private int width;

            private int height;

            public int getWidth()
            {
                return width;
            }

            public void setWidth(int width)
            {
                this.width = width;
            }

            public int getHeight()
            {
                return height;
            }

            public void setHeight(int height)
            {
                this.height = height;
            }
        }
    }

    /**
     * 习网网络请求地址接口
     * 
     * @author bin
     */
    public static interface CWAction
    {

        /**
         * 获取请求地址
         * 
         * @param action
         *            action
         * @return url 网络请求地址
         */
        public String getActionUrl(String action);

    }

    /**
     * 文件服务器action
     * 
     * @author RES-KUNZHU
     * 
     */
    public static interface UploadFileAction extends CWAction
    {
        public void changeUploadServer();
    }

    /**
     * 网络请求获取验证信息接口
     * 
     * @author bin
     * 
     */
    public interface VerifyInfoListener
    {
        /**
         * 
         * 获取验证信息
         * 
         * @return 返回网络请求验证信息
         */
        VerifyInfo getVerifyInfo();
    }

    /**
     * 网络请求回调接口
     * 
     * @author bin
     * 
     */
    public abstract static class ResponseCallback
    {
        /**
         * 
         * 网络请求响应成功
         * 
         * @param responseStr
         *            响应字符串数据
         */
        public void success(String responseStr)
        {

        }

        /**
         * 
         * 网络请求响应成功
         * 
         * @param obj
         * @param resultCode
         *            返回状态码
         * @param errorCode
         *            错误码
         * @param msg
         *            返回状态信息
         */
        public void success(Object obj, int resultCode, int errorCode,
                String msg)
        {
        }

        /**
         * 
         * 网络请求响应成功<br/>
         * 传入调用者指定的xml sax Handler
         * 
         * @param xmlHandler
         *            {@link HttpRequest#XmlHandler}
         */
        public void success(XmlHandler xmlHandler)
        {

        }

        /**
         * 
         * 网络请求响应错误
         * 
         * @param errorCode
         *            错误码
         * @param msg
         *            返回状态信息
         */
        public void error(int errorCode, String msg)
        {

        }
    }

}
